﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using Lazeeboy.FantomG;
using BKSystem.IO;

namespace PhraseOrganizer
{
    public partial class Form1 : Form
    {
        private bool _runningOnWindows;
        private bool _dotNet;

        public Form1()
        {
            InitializeComponent();

            Load();
#if DEBUG
            MessageBox.Show(string.Format("Little Endian: {0}. Windows should be: True; Check it on a Mac.", 
                BitConverter.IsLittleEndian));
#endif

            int p = (int)Environment.OSVersion.Platform;
            if ((p == 4) || (p == 6) || (p == 128))
            {
#if DEBUG
                MessageBox.Show("Running on Unix");
#endif
                _runningOnWindows = false;
            }
            else
            {
#if DEBUG
                MessageBox.Show("Running on Windows");
#endif
                _runningOnWindows = true;
            }

            Type t = Type.GetType("Mono.Runtime");
            if (t != null)
            {
#if DEBUG
                MessageBox.Show("Mono Detected");
#endif
                _dotNet = false;
            }
            else
            {
#if DEBUG
                MessageBox.Show(".NET");
#endif
                _dotNet = true;
            }

        }

        private void txtProjectPath_KeyDown(object sender, KeyEventArgs e)
        {
            e.Handled = true;
            e.SuppressKeyPress = true;
        }

        private void txtProjectPath_KeyPress(object sender, KeyPressEventArgs e)
        {
            e.Handled = true;
        }

        private void btnLoadProject_Click(object sender, EventArgs e)
        {
            string oldPath = folderProjectPath.SelectedPath;

			folderProjectPath.SelectedPath = Environment.CurrentDirectory;
			
            if (folderProjectPath.ShowDialog(this) == DialogResult.OK)	
                Load();
            else
                folderProjectPath.SelectedPath = oldPath;
        }

        private void Load()
        {
            txtProjectPath.Text = folderProjectPath.SelectedPath;

            Phrase.Load(folderProjectPath.SelectedPath);

            RefreshPhraseList();

            RPSSet.Load(folderProjectPath.SelectedPath);

            int id = 0;
            string name = "";

            BitStream bitStream = null;

			string projectFile = System.IO.Path.Combine(folderProjectPath.SelectedPath, 
			                                            string.Format(@"SND{0}FANGSND.SVD", Path.DirectorySeparatorChar));
			if(File.Exists(projectFile))
			{
                bitStream = new BitStream(File.OpenRead(projectFile));

                byte sevenBits;
                char asciiChar;

                for (int byteOffset = 0x100; byteOffset < 0x89C * 512; byteOffset += 0x89C)
                {
                    id++;
                    name = "";

                    if (byteOffset <= bitStream.Length8 - 14)
                    {
                        bitStream.Position = (long)byteOffset * 8;

                        for (int charactorPosition = 0; charactorPosition < 16; charactorPosition++)
                        {
                            bitStream.Read(out sevenBits, 0, 7);
                            asciiChar = (char)sevenBits;

                            if (!Char.IsControl(asciiChar))
                                name += asciiChar;
                        }

                        bitStream.Position = (byteOffset + 0x88F) * 8;

                        short rps = (short)(bitStream.ReadByte() + 1);

                        LiveSet liveSet = LiveSet.AddLiveSetWithRPS(id, name, rps);
                    }
                }
				
				RefreshLiveSetList();
            }
        }

        private void lstPhrases_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch(lstPhrases.SelectedIndices.Count )
            {
                case 0:
                    btnRename.Enabled = false;
                    txtPhraseName.Enabled = false;
                    txtPhraseName.Text = "";

                    btnAlphabetize.Enabled = false;
                    btnDefragment.Enabled = false;
                    break;

                case 1:
                    btnRename.Enabled = true;
                    txtPhraseName.Enabled = true;
                    txtPhraseName.Text = ((Phrase)lstPhrases.SelectedValue).Name;

                    btnAlphabetize.Enabled = false;
                    btnDefragment.Enabled = false;
                    break;

                default:
                    btnRename.Enabled = false;
                    txtPhraseName.Enabled = false;
                    txtPhraseName.Text = "";

                    btnAlphabetize.Enabled = true;
                    btnDefragment.Enabled = true;
                    break;
            }
        }

        private void btnCancelChanges_Click(object sender, EventArgs e)
        {
            Load();
        }

        private void btnApplyChanges_Click(object sender, EventArgs e)
        {
            if(MessageBox.Show(this, 
                "Are you Sure you want to apply the changes?\r\nThis utility is in a very early stage of development so\r\nbe SURE to BACKUP your project BEFORE you click YES.", "Save Changes?", 
                MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
            {
                string UpdatesMade;
                Phrase.Save(folderProjectPath.SelectedPath, out UpdatesMade);

                //if (string.IsNullOrEmpty(UpdatesMade))
                //    MessageBox.Show("No changes were saved.", "No changes were saved.");
                //else
                //    MessageBox.Show(UpdatesMade, "The following changes were saved to the project.");

                Load();
            }
        }

        private void lstPhrases_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            txtPhraseName.Focus();
            txtPhraseName.SelectAll();
        }

        private void lstPhrases_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control)
            {
                switch (e.KeyCode)
                {
                    case Keys.Home:
                        e.Handled = true;
                        MovePhrases(0);
                        break;

                    case Keys.PageUp:
                        e.Handled = true;
                        if(e.Shift)
                            MovePhrases(-100);
                        else
                            MovePhrases(-10);

                        break;

                    case Keys.Up:
                        e.Handled = true;
                        if (e.Shift)
                            MovePhrases(-5);
                        else
                            MovePhrases(-1);

                        break;

                    case Keys.Down:
                        e.Handled = true;
                        if (e.Shift)
                            MovePhrases(5);
                        else
                            MovePhrases(1);

                        break;

                    case Keys.PageDown:
                        e.Handled = true;
                        if (e.Shift)
                            MovePhrases(100);
                        else
                            MovePhrases(10);

                        break;

                    case Keys.End:
                        e.Handled = true;
                        MovePhrases(2000);
                        break;

                    case Keys.A:
                        e.Handled = true;
                        MoveSelection(1, 2000);
                        break;

                    case Keys.D:
                        e.Handled = true;
                        DefragmentSelection();
                        break;

                    case Keys.S:
                        e.Handled = true;
                        AlphabetizeSelection();
                        break;
                }
            }

            if (e.KeyCode == Keys.Delete)
            {
                e.Handled = true;
                DeletePhrases();
            }
        }

        private void txtPhraseName_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
                RenamePhrase();
        }

        private void RenamePhrase()
        {
            Phrase.RenamePhrase(lstPhrases.SelectedIndices[0] + 1, txtPhraseName.Text);

            RefreshPhraseList();
        }

        private void RefreshPhraseList()
        {
            lstPhrases.BeginUpdate();

            lstPhrases.DataSource = null;
            lstPhrases.DataSource = Phrase.Phrases;

            lstPhrases.EndUpdate();
        }

        private void RefreshLiveSetList()
        {
    	    lstLiveSets.BeginUpdate();

	        lstLiveSets.DataSource = null;
            if(LiveSet.LiveSets != null)
            	lstLiveSets.DataSource = LiveSet.LiveSets;

        	lstLiveSets.EndUpdate();
		}

        private void btnRename_Click(object sender, EventArgs e)
        {
            RenamePhrase();
        }

        private List<Phrase> SelectedPhrases()
        {
            List<Phrase> selection = new List<Phrase>();
            foreach (int index in lstPhrases.SelectedIndices)
                selection.Add(Phrase.Phrases[index]);

            selection.Sort();
            return selection;
        }

        private List<LiveSet> SelectedLiveSets()
        {
            List<LiveSet> selection = new List<LiveSet>();

            foreach (int index in lstLiveSets.SelectedIndices)
                selection.Add(LiveSet.LiveSets[index]);

            return selection;
        }

        private void MoveSelection(int StartPosition, int Length)
        {
            lstPhrases.BeginUpdate(); 
            
            if (StartPosition > 0 && Length > 0 && StartPosition + Length - 1 <= 2000)
            {
                lstPhrases.ClearSelected();

                for (int i = StartPosition; i < StartPosition + Length; i++)
                    lstPhrases.SelectedItems.Add(lstPhrases.Items[i - 1]);
            }

            lstPhrases.EndUpdate();
        }

        private void MovePhrases(int Movement)
        {
            if (lstPhrases.SelectedIndices.Count > 0)
            {
                int StartPosition, Length;
                int EndPosition;

                if (Phrase.GetContiguousSelectedIndices(SelectedPhrases(), out StartPosition, out Length))
                {
                    switch (Movement)
                    {
                        case 0:
                            EndPosition = 1;
                            break;

                        case 2000:
                            EndPosition = 2000 - Length + 1;
                            break;

                        default:
                            EndPosition = StartPosition + Movement;
                            break;
                    }

                    Phrase.MoveSelection(StartPosition, Length, EndPosition);

                    RefreshPhraseList();
                    MoveSelection(EndPosition, Length);
                }
            }
        }

        private void DeletePhrases()
        {
            if (lstPhrases.SelectedIndices.Count > 0)
            {
                List<Phrase> phrases = SelectedPhrases();
                if (phrases.Count > 0 && MessageBox.Show(this, "Delete selected phrases?", "Delete", 
                    MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    foreach (Phrase phrase in phrases)
                        Phrase.DeletePhrase(phrase);

                    RefreshPhraseList();
                }
            }

        }

        private void AlphabetizeSelection()
        {
            if (lstPhrases.SelectedIndices.Count > 0)
            {
                int StartPosition, Length;

                if (Phrase.GetContiguousSelectedIndices(SelectedPhrases(), out StartPosition, out Length))
                {
                    Phrase.Alphabetize(StartPosition, Length);
                    RefreshPhraseList();

                    MoveSelection(StartPosition, Length);
                }
            }
        }

        private void btnTop_Click(object sender, EventArgs e)
        {
            MovePhrases(0);
        }

        private void btnUp_Click(object sender, EventArgs e)
        {
            MovePhrases(-1);
        }

        private void btnDown_Click(object sender, EventArgs e)
        {
            MovePhrases(1);
        }

        private void btnBottom_Click(object sender, EventArgs e)
        {
            MovePhrases(2000);
        }

        private void btnDelete_Click(object sender, EventArgs e)
        {
            DeletePhrases();
        }

        private void btnDefragment_Click(object sender, EventArgs e)
        {
            DefragmentSelection();
        }

        private void DefragmentSelection()
        {
            if (lstPhrases.SelectedIndices.Count > 0)
            {
                int StartPosition, Length;

                if (Phrase.GetContiguousSelectedIndices(SelectedPhrases(), out StartPosition, out Length))
                {
                    Phrase.Defragment(StartPosition, Length);
                    RefreshPhraseList();

                    MoveSelection(StartPosition, Length);
                }
            }
        }

        private void btnAlphabetize_Click(object sender, EventArgs e)
        {
            AlphabetizeSelection();
        }

        private void btnGenerateLivesetPhrases_Click(object sender, EventArgs e)
        {
            if (lstPhrases.SelectedIndices.Count > 0)
            {
                int StartPosition, Length;

                if (Phrase.GetContiguousSelectedIndices(SelectedPhrases(), out StartPosition, out Length))
                {
                    byte[] liveSetPhraseBytes = File.ReadAllBytes("LiveSetChangeTemplate.SVP");

                    List<LiveSet> liveSets = SelectedLiveSets();

                    if (liveSets != null && liveSets.Count > 0)
                    {
                        short Position = (short)StartPosition;

                        foreach (LiveSet liveSet in liveSets)
                        {
                            if (Position < StartPosition + Length)
                            {
                                // inject livesetname as phrasename
                                byte[] phraseNameBytes = new byte[16];
                                phraseNameBytes = Encoding.ASCII.GetBytes(liveSet.Name);
                                int phraseNameUpperBound = phraseNameBytes.GetUpperBound(0);
                                for (int i = 0; i < 16; i++)
                                {
                                    if (i <= phraseNameUpperBound)
                                        liveSetPhraseBytes[i + 0xE] = phraseNameBytes[i];
                                    else
                                        // fill up with spaces
                                        liveSetPhraseBytes[i + 0xE] = 0x20;
                                }

                                // inject program change
                                ushort programChange = (ushort)(liveSet.ID - 1);
                                byte[] programChangeBytes = BitConverter.GetBytes(programChange);

                                // todo: test this; what's the endianness on a mac?
                                if (BitConverter.IsLittleEndian)
                                {
                                    // windows
                                    liveSetPhraseBytes[164] = programChangeBytes[1];
                                    liveSetPhraseBytes[165] = programChangeBytes[0];
                                }
                                else
                                {
                                    // mac?
                                    liveSetPhraseBytes[164] = programChangeBytes[0];
                                    liveSetPhraseBytes[165] = programChangeBytes[1];
                                }

								// save the phrase to our list
                                Phrase.AddPhrase(Position, liveSet.Name, (byte[])liveSetPhraseBytes.Clone());

                                Position++;
                            }
                            else break;
                        }
                    }
                    
                    RefreshPhraseList();
                }
            }
        }
    }
}
